/*
REWRITTEN BY XINEF
*/

#include "ScriptMgr.h"
#include "ScriptedCreature.h"
#include "the_eye.h"
#include "WaypointManager.h"
#include "MoveSplineInit.h"

enum Spells
{
	SPELL_BERSERK					= 45078,
	SPELL_FLAME_QUILLS				= 34229,
	SPELL_QUILL_MISSILE_1			= 34269, // 21
	SPELL_QUILL_MISSILE_2			= 34314, // 3
	SPELL_FLAME_BUFFET				= 34121,
	SPELL_EMBER_BLAST				= 34341,
	SPELL_REBIRTH_PHASE2			= 34342,
	SPELL_MELT_ARMOR				= 35410,
	SPELL_CHARGE					= 35412,
	SPELL_REBIRTH_DIVE				= 35369,
	SPELL_DIVE_BOMB_VISUAL			= 35367,
	SPELL_DIVE_BOMB					= 35181
};

const Position alarPoints[7] =
{
	{340.15f, 58.65f, 17.71f, 4.60f},
    {388.09f, 31.54f, 20.18f, 1.61f},
    {388.18f, -32.85f, 20.18f, 0.52f},
    {340.29f, -60.19f, 17.72f, 5.71f},
    {332.0f, 0.01f, 43.0f, 0.0f},
    {331.0f, 0.01f, -2.38f, 0.0f},
    {332.0f, 0.01f, 43.0f, 0.0f}
};

enum Misc
{
	DISPLAYID_INVISIBLE			= 23377,
	NPC_EMBER_OF_ALAR			= 19551,
	NPC_FLAME_PATCH				= 20602,
    
	POINT_PLATFORM				= 0,
	POINT_QUILL					= 4,
	POINT_MIDDLE				= 5,
	POINT_DIVE					= 6,

	EVENT_SWITCH_PLATFORM		= 1,
	EVENT_START_QUILLS			= 2,
	EVENT_RELOCATE_MIDDLE		= 3,
	EVENT_REBIRTH				= 4,
	EVENT_SPELL_MELT_ARMOR		= 5,
	EVENT_SPELL_FLAME_PATCH		= 6,
	EVENT_SPELL_CHARGE			= 7,
	EVENT_SPELL_DIVE_BOMB		= 8,
	EVENT_START_DIVE			= 9,
	EVENT_CAST_DIVE_BOMB		= 10,
	EVENT_SUMMON_DIVE_PHOENIX	= 11,
	EVENT_REBIRTH_DIVE			= 12,
	EVENT_SPELL_BERSERK			= 13,

	EVENT_MOVE_TO_PHASE_2		= 20,
	EVENT_FINISH_DIVE			= 21

};

// Xinef: Ruse of the Ashtongue (10946)
enum qruseoftheAshtongue
{
	SPELL_ASHTONGUE_RUSE		= 42090,
	QUEST_RUSE_OF_THE_ASHTONGUE = 10946,
};

class boss_alar : public CreatureScript
{
    public:
        boss_alar() : CreatureScript("boss_alar") { }

        struct boss_alarAI : public BossAI
        {
            boss_alarAI(Creature* creature) : BossAI(creature, DATA_ALAR)
            {
				startPath = true;
				SetCombatMovement(false);
            }

			uint8 platform;
			uint8 noQuillTimes;
			bool startPath;

			void JustReachedHome()
			{
				BossAI::JustReachedHome();
				startPath = true;
			}

            void Reset()
            {
				BossAI::Reset();
				platform = 0;
				noQuillTimes = 0;
				me->SetModelVisible(true);
                me->ApplySpellImmune(0, IMMUNITY_DAMAGE, SPELL_SCHOOL_MASK_FIRE, true);
				me->SetReactState(REACT_AGGRESSIVE);
            }

            void EnterCombat(Unit* who)
            {
				BossAI::EnterCombat(who);
				events.ScheduleEvent(EVENT_SWITCH_PLATFORM, 0);
            }

            void JustDied(Unit* killer)
            {
				me->SetModelVisible(true);
				BossAI::JustDied(killer);

				// Xinef: Ruse of the Ashtongue (10946)
				Map::PlayerList const& pl = me->GetMap()->GetPlayers();
				for (Map::PlayerList::const_iterator itr = pl.begin(); itr != pl.end(); ++itr)
				{
					Player* player = itr->GetSource();
					if (player->GetQuestStatus(QUEST_RUSE_OF_THE_ASHTONGUE) == QUEST_STATUS_INCOMPLETE)
						if (player->HasAura(SPELL_ASHTONGUE_RUSE))
							player->AreaExploredOrEventHappens(QUEST_RUSE_OF_THE_ASHTONGUE);
				}
            }

            void JustSummoned(Creature* summon)
            {
				summons.Summon(summon);
                if (summon->GetEntry() == NPC_EMBER_OF_ALAR)	
					summon->ApplySpellImmune(0, IMMUNITY_DAMAGE, SPELL_SCHOOL_MASK_FIRE, true);
            }

            void MoveInLineOfSight(Unit* /*who*/) { }

            void DamageTaken(Unit*, uint32& damage, DamageEffectType, SpellSchoolMask)
            {
                if (damage >= me->GetHealth() && platform < POINT_MIDDLE)
                {
                    damage = 0;
					if (events.GetNextEventTime(EVENT_REBIRTH) == 0)
					{
						me->InterruptNonMeleeSpells(false);
						me->SetHealth(me->GetMaxHealth());
						me->SetReactState(REACT_PASSIVE);
						me->CastSpell(me, SPELL_EMBER_BLAST, true);

						me->setAttackTimer(BASE_ATTACK, 16000);
						events.Reset();
						events.ScheduleEvent(EVENT_RELOCATE_MIDDLE, 8000);
						events.ScheduleEvent(EVENT_MOVE_TO_PHASE_2, 12000);
						events.ScheduleEvent(EVENT_REBIRTH, 16001);
					}
                }
            }

            void MovementInform(uint32 type, uint32 id)
            {
				if (type != POINT_MOTION_TYPE)
				{
					if (type == ESCORT_MOTION_TYPE && me->movespline->Finalized() && !me->IsInCombat())
						startPath = true;
					return;
				}

				if (id == POINT_PLATFORM)
					me->setAttackTimer(BASE_ATTACK, 1000);
				else if (id == POINT_QUILL)
					events.ScheduleEvent(EVENT_START_QUILLS, 1000);
				else if (id == POINT_DIVE)
				{
					events.ScheduleEvent(EVENT_START_DIVE, 1000);
					events.ScheduleEvent(EVENT_CAST_DIVE_BOMB, 5000);
				}
            }

            void UpdateAI(uint32 diff)
            {
				if (startPath)
				{
					me->StopMoving();
					startPath = false;
					if (WaypointPath const* i_path = sWaypointMgr->GetPath(me->GetWaypointPath()))
					{
						Movement::PointsArray pathPoints;
						pathPoints.push_back(G3D::Vector3(me->GetPositionX(), me->GetPositionY(), me->GetPositionZ()));
						for (uint8 i = 0; i < i_path->size(); ++i)
						{
							WaypointData const* node = i_path->at(i);
							pathPoints.push_back(G3D::Vector3(node->x, node->y, node->z));
						}
						me->GetMotionMaster()->MoveSplinePath(&pathPoints);
					}
				}

				if (!UpdateVictim())
					return;

				events.Update(diff);
				if (me->HasUnitState(UNIT_STATE_CASTING))
					return;

				switch (events.ExecuteEvent())
				{
					case EVENT_SWITCH_PLATFORM:
						if (roll_chance_i(20*noQuillTimes))
						{
							noQuillTimes = 0;
							platform = RAND(0, 3);
							me->GetMotionMaster()->MovePoint(POINT_QUILL, alarPoints[POINT_QUILL], false, true);
							events.ScheduleEvent(EVENT_SWITCH_PLATFORM, 16000);
						}
						else
						{
							if (noQuillTimes++ > 0)
							{
								me->SetOrientation(alarPoints[platform].GetOrientation());
								me->SummonCreature(NPC_EMBER_OF_ALAR, *me, TEMPSUMMON_CORPSE_TIMED_DESPAWN, 6000);
							}
							me->GetMotionMaster()->MovePoint(POINT_PLATFORM, alarPoints[platform], false, true);
							platform = (++platform)%4;
							events.ScheduleEvent(EVENT_SWITCH_PLATFORM, 30000);
						}
						me->setAttackTimer(BASE_ATTACK, 20000);
						break;
					case EVENT_START_QUILLS:
						me->CastSpell(me, SPELL_FLAME_QUILLS, false);
						break;
					case EVENT_RELOCATE_MIDDLE:
						me->SetPosition(alarPoints[POINT_MIDDLE]);
						break;
					case EVENT_MOVE_TO_PHASE_2:
						me->RemoveAurasDueToSpell(SPELL_EMBER_BLAST);
						me->CastSpell(me, SPELL_REBIRTH_PHASE2, false);
						break;
					case EVENT_REBIRTH:
						me->SetReactState(REACT_AGGRESSIVE);
						platform = POINT_MIDDLE;
						me->GetMotionMaster()->MoveChase(me->GetVictim());

						events.ScheduleEvent(EVENT_SPELL_MELT_ARMOR, 67000);
						events.ScheduleEvent(EVENT_SPELL_CHARGE, 10000);
						events.ScheduleEvent(EVENT_SPELL_FLAME_PATCH, 20000);
						events.ScheduleEvent(EVENT_SPELL_DIVE_BOMB, 30000);
						break;
					case EVENT_SPELL_MELT_ARMOR:
						me->CastSpell(me->GetVictim(), SPELL_MELT_ARMOR, false);
						events.ScheduleEvent(EVENT_SPELL_MELT_ARMOR, 60000);
						break;
					case EVENT_SPELL_CHARGE:
						if (Unit* target = SelectTarget(SELECT_TARGET_RANDOM, 0, 50.0f, true))
							me->CastSpell(target, SPELL_CHARGE, false);
						events.ScheduleEvent(EVENT_SPELL_CHARGE, 30000);
						break;
					case EVENT_SPELL_FLAME_PATCH:
						if (Unit* target = SelectTarget(SELECT_TARGET_RANDOM, 0, 50.0f, true))
							me->SummonCreature(NPC_FLAME_PATCH, *target, TEMPSUMMON_TIMED_DESPAWN, 2*MINUTE*IN_MILLISECONDS);
						events.ScheduleEvent(EVENT_SPELL_FLAME_PATCH, 30000);
						break;
					case EVENT_SPELL_DIVE_BOMB:
						me->GetMotionMaster()->MovePoint(POINT_DIVE, alarPoints[POINT_DIVE], false, true);
						events.ScheduleEvent(EVENT_SPELL_DIVE_BOMB, 30000);
						events.DelayEvents(15000);
						me->setAttackTimer(BASE_ATTACK, 20000);
						break;
					case EVENT_START_DIVE:
						me->CastSpell(me, SPELL_DIVE_BOMB_VISUAL, false);
						break;
					case EVENT_CAST_DIVE_BOMB:
						events.ScheduleEvent(EVENT_SUMMON_DIVE_PHOENIX, 2000);
						events.ScheduleEvent(EVENT_REBIRTH_DIVE, 6000);
						events.ScheduleEvent(EVENT_FINISH_DIVE, 10000);
						if (Unit* target = SelectTarget(SELECT_TARGET_RANDOM, 0, 90.0f, true))
						{
							me->CastSpell(target, SPELL_DIVE_BOMB, false);
							me->SetPosition(*target);
							me->StopMovingOnCurrentPos();
						}

						me->RemoveAurasDueToSpell(SPELL_DIVE_BOMB_VISUAL);
						break;
					case EVENT_SUMMON_DIVE_PHOENIX:
					{
						Unit* target = SelectTarget(SELECT_TARGET_RANDOM, 0, 10.0f, true);
						me->SummonCreature(NPC_EMBER_OF_ALAR, target ? *target : *me, TEMPSUMMON_CORPSE_TIMED_DESPAWN, 6000);
						me->SummonCreature(NPC_EMBER_OF_ALAR, target ? *target : *me, TEMPSUMMON_CORPSE_TIMED_DESPAWN, 6000);
						break;
					}
					case EVENT_REBIRTH_DIVE:
						me->SetModelVisible(true);
						me->CastSpell(me, SPELL_REBIRTH_DIVE, false);
						break;
					case EVENT_FINISH_DIVE:
						me->GetMotionMaster()->MoveChase(me->GetVictim());
						break;
					case EVENT_SPELL_BERSERK:
						me->CastSpell(me, SPELL_BERSERK, true);
						break;
				}

				if (me->isAttackReady())
				{
                    if (me->IsWithinMeleeRange(me->GetVictim()))
                    {
                        me->AttackerStateUpdate(me->GetVictim());
                        me->resetAttackTimer();
                    }
                    else
                    {
                        me->resetAttackTimer();
						ThreatContainer::StorageType const &threatList = me->getThreatManager().getThreatList();
						for (ThreatContainer::StorageType::const_iterator itr = threatList.begin(); itr != threatList.end(); ++itr)
							if (Unit* unit = ObjectAccessor::GetUnit(*me, (*itr)->getUnitGuid()))
								if (me->IsWithinMeleeRange(unit))
								{
									me->AttackerStateUpdate(unit);
									return;
								}

						me->CastSpell(me, SPELL_FLAME_BUFFET, false);
                    }
                }
            }
        };

        CreatureAI* GetAI(Creature* creature) const
        {
            return GetInstanceAI<boss_alarAI>(creature);
        }
};

class CastQuill : public BasicEvent
{
    public:
        CastQuill(Unit* caster, uint32 spellId) : _caster(caster), _spellId(spellId)
        {
        }

        bool Execute(uint64 /*execTime*/, uint32 /*diff*/)
        {
			_caster->CastSpell(_caster, _spellId, true);
            return true;
        }

    private:
        Unit* _caster;
		uint32 _spellId;
};

class spell_alar_flame_quills : public SpellScriptLoader
{
    public:
        spell_alar_flame_quills() : SpellScriptLoader("spell_alar_flame_quills") { }

        class spell_alar_flame_quills_AuraScript : public AuraScript
        {
            PrepareAuraScript(spell_alar_flame_quills_AuraScript);

            void HandlePeriodic(AuraEffect const* aurEff)
            {
                PreventDefaultAction();

				// 24 spells in total
				for (uint8 i = 0; i < 21; ++i)
					GetUnitOwner()->m_Events.AddEvent(new CastQuill(GetUnitOwner(), SPELL_QUILL_MISSILE_1+i), GetUnitOwner()->m_Events.CalculateTime(i*40));
				GetUnitOwner()->m_Events.AddEvent(new CastQuill(GetUnitOwner(), SPELL_QUILL_MISSILE_2+0), GetUnitOwner()->m_Events.CalculateTime(22*40));
				GetUnitOwner()->m_Events.AddEvent(new CastQuill(GetUnitOwner(), SPELL_QUILL_MISSILE_2+1), GetUnitOwner()->m_Events.CalculateTime(23*40));
				GetUnitOwner()->m_Events.AddEvent(new CastQuill(GetUnitOwner(), SPELL_QUILL_MISSILE_2+2), GetUnitOwner()->m_Events.CalculateTime(24*40));
            }

            void Register()
            {
                OnEffectPeriodic += AuraEffectPeriodicFn(spell_alar_flame_quills_AuraScript::HandlePeriodic, EFFECT_0, SPELL_AURA_PERIODIC_TRIGGER_SPELL);
            }
        };

        AuraScript* GetAuraScript() const
        {
            return new spell_alar_flame_quills_AuraScript();
        }
};

class spell_alar_ember_blast : public SpellScriptLoader
{
    public:
        spell_alar_ember_blast() : SpellScriptLoader("spell_alar_ember_blast") { }

        class spell_alar_ember_blast_SpellScript : public SpellScript
        {
            PrepareSpellScript(spell_alar_ember_blast_SpellScript);

            void HandleForceCast(SpellEffIndex effIndex)
            {
				PreventHitEffect(effIndex);
				if (InstanceScript* instance = GetCaster()->GetInstanceScript())
					if (Creature* alar = ObjectAccessor::GetCreature(*GetCaster(), instance->GetData64(NPC_ALAR)))
						Unit::DealDamage(GetCaster(), alar, alar->CountPctFromMaxHealth(2));
            }

            void Register()
            {
                OnEffectHitTarget += SpellEffectFn(spell_alar_ember_blast_SpellScript::HandleForceCast, EFFECT_2, SPELL_EFFECT_FORCE_CAST);
            }
        };

        SpellScript* GetSpellScript() const
        {
            return new spell_alar_ember_blast_SpellScript();
        }
};

class spell_alar_ember_blast_death : public SpellScriptLoader
{
	public:
		spell_alar_ember_blast_death() : SpellScriptLoader("spell_alar_ember_blast_death") { }

		class spell_alar_ember_blast_death_AuraScript : public AuraScript
		{
			PrepareAuraScript(spell_alar_ember_blast_death_AuraScript);

			void OnApply(AuraEffect const* aurEff, AuraEffectHandleModes /*mode*/)
			{
				PreventDefaultAction(); // xinef: prevent default action after change that invisibility in instances is executed instantly even for creatures
				Unit* target = GetTarget();
				InvisibilityType type = InvisibilityType(aurEff->GetMiscValue());
				target->m_invisibility.AddFlag(type);
				target->m_invisibility.AddValue(type, aurEff->GetAmount());

				GetUnitOwner()->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE);
				GetUnitOwner()->SetStandState(UNIT_STAND_STATE_DEAD);
				GetUnitOwner()->m_last_notify_position.Relocate(0.0f, 0.0f, 0.0f);
				GetUnitOwner()->m_delayed_unit_relocation_timer = 1000;
			}

			void OnRemove(AuraEffect const* /*aurEff*/, AuraEffectHandleModes /*mode*/)
			{
				GetUnitOwner()->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE);
				GetUnitOwner()->SetStandState(UNIT_STAND_STATE_STAND);
			}

			void Register()
			{
				OnEffectApply += AuraEffectApplyFn(spell_alar_ember_blast_death_AuraScript::OnApply, EFFECT_2, SPELL_AURA_MOD_INVISIBILITY, AURA_EFFECT_HANDLE_REAL);
				OnEffectRemove += AuraEffectRemoveFn(spell_alar_ember_blast_death_AuraScript::OnRemove, EFFECT_2, SPELL_AURA_MOD_INVISIBILITY, AURA_EFFECT_HANDLE_REAL);
			}
		};

		AuraScript* GetAuraScript() const
		{
			return new spell_alar_ember_blast_death_AuraScript();
		}
};

class spell_alar_dive_bomb : public SpellScriptLoader
{
	public:
		spell_alar_dive_bomb() : SpellScriptLoader("spell_alar_dive_bomb") { }

		class spell_alar_dive_bomb_AuraScript : public AuraScript
		{
			PrepareAuraScript(spell_alar_dive_bomb_AuraScript);

			void OnApply(AuraEffect const* /*aurEff*/, AuraEffectHandleModes /*mode*/)
			{
				GetUnitOwner()->SetModelVisible(false);
				GetUnitOwner()->SetDisplayId(DISPLAYID_INVISIBLE);
			}

			void Register()
			{
				OnEffectApply += AuraEffectApplyFn(spell_alar_dive_bomb_AuraScript::OnApply, EFFECT_0, SPELL_AURA_DUMMY, AURA_EFFECT_HANDLE_REAL);
			}
		};

		AuraScript* GetAuraScript() const
		{
			return new spell_alar_dive_bomb_AuraScript();
		}
};

void AddSC_boss_alar()
{
    new boss_alar();
	new spell_alar_flame_quills();
	new spell_alar_ember_blast();
	new spell_alar_ember_blast_death();
	new spell_alar_dive_bomb();
}
